package de.blitzcoder.collide;

import com.apple.eawt.Application;
import com.apple.eawt.ApplicationAdapter;
import com.apple.eawt.ApplicationEvent;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import de.blitzcoder.collide.config.ConfigDialog;
import de.blitzcoder.collide.engine.FileOpener;
import de.blitzcoder.collide.gui.BlitzMaxCodeDocument;
import de.blitzcoder.collide.gui.CodeDocument;
import de.blitzcoder.collide.gui.CreditsDialog;
import de.blitzcoder.collide.gui.DockingDocumentManager;
import de.blitzcoder.collide.gui.Document;
import de.blitzcoder.collide.gui.DocumentManager;
import de.blitzcoder.collide.gui.DocumentManagerListener;
import de.blitzcoder.collide.gui.FileDropTargetListener;
import de.blitzcoder.collide.gui.IDEFrame;
import de.blitzcoder.collide.gui.TabbedDocumentManager;
import de.blitzcoder.collide.gui.codetree.CodeTree;
import de.blitzcoder.collide.gui.docs.DocumentationPanel;
import de.blitzcoder.collide.gui.fileexplorer.FileExplorer;
import de.blitzcoder.collide.util.Log;
import de.blitzcoder.collide.util.OS;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.dnd.DropTarget;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.AccessControlException;
import java.util.HashMap;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import org.noos.xing.mydoggy.ToolWindow;
import org.noos.xing.mydoggy.ToolWindowAnchor;
import org.noos.xing.mydoggy.ToolWindowManager;
import org.noos.xing.mydoggy.plaf.MyDoggyToolWindowManager;

public class Interface extends IDEFrame {

    // Menus
    private Menu menu;

    // ActionToolBar
    public ActionToolBar toolBar;

    // File and ID to save dockable Window positions
    public static final String PERSISTANCE_FILE = "interface.xml";

    // Docking IDs
    public static final String FILEMANAGER = "DateiManager";
    public static final String OUTPUT = "Output";
    public static final String CODETREE = "CodeTree";
    public static final String DOCUMENTATION = "Dokumentation";

    // Dockables
    private HashMap<String, JComponent> components;

    // the documentmanager's
    private DocumentManager documentManager;
    private DocumentManager outputDocumentManager;

    // Docking
    private ToolWindowManager toolWindowManager;

    /*
     * Init the Interface
     */
    public static Interface iface = null;

    public static void init() {
        iface = new Interface();
    }

    public Interface() {

        super("CollIDE Version " + CollIDE.VERSION, false);

        Log.log("Creating Interface");

        if (OS.isMacOSX()) {
            Application.getApplication().addApplicationListener(new ApplicationAdapter() {

                public void handleQuit(ApplicationEvent e) {
                    End.exitIDE();
                    e.setHandled(false);
                }

                public void handleAbout(ApplicationEvent event) {
                    new CreditsDialog();
                }

                public void handleOpenApplication(ApplicationEvent event) {
                    // TODO: Schauen was der Filename ist event.getFileName()
                }

                public void handleOpenFile(ApplicationEvent event) {
                    FileOpener.open(new File(event.getFilename()));
                }

                public void handlePreferences(ApplicationEvent event) {
                    new ConfigDialog().setVisible(true);
                }

                public void handlePrintFile(ApplicationEvent event) {
                    JOptionPane.showMessageDialog(Interface.get(), "Printing not supported.");
                }

                public void handleReOpenApplication(ApplicationEvent event) {
                    Log.log("handleReOpenApplication called.");
                }
            });
        }

        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);

        // create the menu
        menu = new Menu();
        setJMenuBar(menu);

        // Create the toolbar
        toolBar = new ActionToolBar();

        // Create the ToolWindowManager
        MyDoggyToolWindowManager myDoggyToolWindowManager = new MyDoggyToolWindowManager();
        toolWindowManager = myDoggyToolWindowManager;

        createDockables();

        // Register the tools
        toolWindowManager.registerToolWindow(
                CODETREE,
                "CodeTree",
                null, // Icon
                getComponent(CODETREE),
                ToolWindowAnchor.RIGHT);

        toolWindowManager.registerToolWindow(
                DOCUMENTATION,
                "Dokumentation",
                null, // Icon
                getComponent(DOCUMENTATION),
                ToolWindowAnchor.BOTTOM);

        toolWindowManager.registerToolWindow(
                FILEMANAGER,
                "Dateimanager",
                null, // Icon
                getComponent(FILEMANAGER),
                ToolWindowAnchor.LEFT);

        final ToolWindow outputToolWindow = toolWindowManager.registerToolWindow(
                OUTPUT,
                "Ausgabe",
                null, // Icon
                getComponent(OUTPUT),
                ToolWindowAnchor.BOTTOM);

        outputDocumentManager.addDocumentManagerListener(new DocumentManagerListener() {

            public void documentAdded(Document doc) {
                outputToolWindow.ensureVisible();
                outputToolWindow.setActive(true);
            }

            public void documentRemoved(Document doc) {
            }

            public void documentSelected(Document doc) {
            }
        });


        // Make ToolWindows available
        for (ToolWindow w : toolWindowManager.getToolWindows()) {

            // Workaround für OSX
            // Wenn Opaque = true, dann erscheinen die Tools
            // wirklich transparent
            Component comp = w.getComponent();
            if (comp instanceof JComponent) {
                JComponent jComp = (JComponent) comp;
                jComp.setOpaque(false);
            }

            w.setAvailable(true);
        }

        // Setup DragDrop
        // Um Dateien ins Fenster zu ziehen
        DropTarget t = new DropTarget();
        try {
            t.addDropTargetListener(new FileDropTargetListener());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        setDropTarget(t);

        setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        c.weightx = 1.0;
        c.anchor = c.LINE_START;
        add(toolBar, c);
        c.fill = c.BOTH;
        c.weighty = 1.0;
        c.gridy = 1;
        add(myDoggyToolWindowManager, c);

        loadWindowSize();

        pack();

        loadPersistentLayout();


        addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                End.exitIDE();

            }
        });

    }

    public void savePersistentLayout() {
        try {
            toolWindowManager.getPersistenceDelegate().save(new FileOutputStream(PERSISTANCE_FILE));
            saveWindowSize();
            saveCompileFlags();
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    public void saveWindowSize() {
        Config.setRectangleProperty("ui.bounds", getBounds());
        Config.setBooleanProperty("ui.maximized", getExtendedState() == MAXIMIZED_BOTH);
    }

    public void loadWindowSize() {
        Rectangle rect = Config.getRectangleProperty("ui.bounds");
        setPreferredSize(new Dimension(rect.width, rect.height));
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        if (rect.x == -1) {
            rect.x = screenSize.width / 2 - rect.width / 2;
        }
        if (rect.y == -1) {
            rect.y = screenSize.height / 2 - rect.height / 2;
        }
        setLocation(rect.x, rect.y);
        if (Config.getBooleanProperty("ui.maximized")) {
            setExtendedState(MAXIMIZED_BOTH);
        }
    }

    private void loadCompileFlags() {
        toolBar.actionBuildDebug.setSelected(Config.getBooleanProperty("bmax.build.debug"));
        toolBar.actionBuildQuick.setSelected(Config.getBooleanProperty("bmax.build.quick"));
        toolBar.actionBuildGuiApp.setSelected(Config.getBooleanProperty("bmax.build.gui"));
    }
    
    private void saveCompileFlags() {
        Config.setBooleanProperty("bmax.build.debug", toolBar.actionBuildDebug.isSelected());
        Config.setBooleanProperty("bmax.build.quick", toolBar.actionBuildQuick.isSelected());
        Config.setBooleanProperty("bmax.build.gui", toolBar.actionBuildGuiApp.isSelected());
    }

    public void loadPersistentLayout() {
        try {
            toolWindowManager.getPersistenceDelegate().apply(new FileInputStream(PERSISTANCE_FILE));
            loadCompileFlags();
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    public Menu getMenu() {
        return menu;
    }

    public static void setLookAndFeel() {
        String laf = Config.getProperty("ui.lookandfeel");
        if (laf == null) {
            laf = UIManager.getCrossPlatformLookAndFeelClassName();
        }
        setLookAndFeel(laf);
    }

    public static void setLookAndFeel(String laf) {

        // if this is a mac and the systemLAF is selected, use quaqua
        if (OS.isMacOSX()) {

            System.setProperty("Quaqua.tabLayoutPolicy", "scroll"); // OSX-like Tabs
            // Use screen menu bar, if not switched off explicitly
            try {
                if (System.getProperty("apple.laf.useScreenMenuBar") == null &&
                        System.getProperty("com.apple.macos.useScreenMenuBar") == null) {
                    System.setProperty("apple.laf.useScreenMenuBar", "true");
                    System.setProperty("com.apple.macos.useScreenMenuBar", "true");
                }
            } catch (AccessControlException e) {
                // can't do anything about this
            }

        }


        // Try if laf is available, if not use standard laf
        try {
            UIManager.setLookAndFeel(laf);
        } catch (Exception ex) {
            Log.log("Unable to set LookAndFeel " + laf + " (" + ex.getClass().getName() + ")");
        }


    }

    /*
     * Returns a screenshot of the frame as byte[] (JPEG)
     */
    public byte[] screenshot() {
        int height = this.getHeight();
        int width = this.getWidth();
        BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        this.paint(img.getGraphics());
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
            JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(img);
            param.setQuality(0.75f, true);
            encoder.encode(img, param);
            out.close();
            byte[] jpeg = out.toByteArray();

            return jpeg;
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;

    }

    public static DocumentManager getDocumentManager() {
        return get().documentManager;
    }

    private Component getComponent(String id) {
        return components.get(id);
    }

    public static CodeTree getCodeTree() {
        return (CodeTree) get().getComponent(CODETREE);
    }

    public static DocumentManager getOutput() {
        return get().outputDocumentManager;
    }

    public static DocumentationPanel getDocumentation() {
        return (DocumentationPanel) get().getComponent(DOCUMENTATION);
    }

    public static Interface get() {
        return iface;
    }

    // This should be used by dialogs
    public static JFrame getDialogParent() {
        return iface == null && iface.isVisible() ? CollIDE.splash : iface;
    }

    // to implement
    public static void setStatusStopped() {
        get().toolBar.actionStatus.setIcon(get().toolBar.actionStatusStopped);
    }

    public static void setStatusRunning() {
        get().toolBar.actionStatus.setIcon(get().toolBar.actionStatusRunning);
    }

    public static boolean getBuildDebug() {
        return get().toolBar.actionBuildDebug.isSelected();
    }

    public static boolean getBuildQuick() {
        return get().toolBar.actionBuildQuick.isSelected();
    }

    public static boolean getBuildGuiApp() {
        return get().toolBar.actionBuildGuiApp.isSelected();
    }

    public static void updateSelectBuildFileComboBox() {
        String selected = (String) get().toolBar.actionSelectBuildFile.getSelectedItem();
        DefaultComboBoxModel model = (DefaultComboBoxModel) get().toolBar.actionSelectBuildFile.getModel();

        model.removeAllElements();

        model.addElement("Aktueller Code");
        Document[] docs = getDocumentManager().getAllDocuments();
        for (Document doc : docs) {
            if (doc instanceof BlitzMaxCodeDocument) {
                BlitzMaxCodeDocument codeDoc = (BlitzMaxCodeDocument) doc;
                model.addElement(codeDoc.getFile().getName());
                Log.log("Adding " + doc.getTitle());
            }
        }

        get().toolBar.actionSelectBuildFile.setSelectedIndex(0);
        for (int i = 0; i < model.getSize(); i++) {
            if (((String) model.getElementAt(i)).equals(selected)) {
                get().toolBar.actionSelectBuildFile.setSelectedIndex(i);
                break;
            }
        }

    }

    public static void selectLineInFile(File file, final int line) {

        DocumentManager dm = getDocumentManager();
        Document doc = dm.getDocumentForFile(file);

        if (doc != null) {
            dm.bringToFront(doc);
        } else {
            try {
                Object obj = FileOpener.open(file);
                if (obj instanceof CodeDocument) {
                    doc = (CodeDocument) obj;
                } else {
                    throw new Error("Strange Thing...");
                }
            } catch (Exception ex) {
                throw new Error(ex);
            }
        }

        if (doc != null && doc instanceof CodeDocument) {
            final CodeDocument codeDoc = (CodeDocument) doc;

            doc.schedule(new Runnable() {

                public void run() {
                    codeDoc.getTextArea().setCaretPosition(codeDoc.getTextArea().getLineStartOffset(line - 1));
                    codeDoc.getTextArea().selectLine();
                }
            });

        } else {
            throw new Error("Document is no CodeDocument");
        }
    }

    public ToolWindowManager getToolWindowManager() {
        return toolWindowManager;
    }

    public static int getBuildCodeIndex() {
        return get().toolBar.actionSelectBuildFile.getSelectedIndex();
    }

    public static void setBuildCode(String name) {
        get().toolBar.actionSelectBuildFile.setSelectedItem(name);
    }

    private void createDockables() {

        components = new HashMap<String, JComponent>();

        documentManager = new DockingDocumentManager(toolWindowManager.getContentManager());
        outputDocumentManager = new TabbedDocumentManager();

        components.put(CODETREE, new CodeTree(documentManager));
        components.put(DOCUMENTATION, new DocumentationPanel());
        components.put(FILEMANAGER, new FileExplorer());
        components.put(OUTPUT, outputDocumentManager.getComponent());

    }
}
